﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace ALProtocol.Tcp
{
    /// <summary>
    ///  表示Tcp服务端。
    /// </summary>
    public class TcpServer : IDisposable
    {
        /// <summary>
        ///  服务端socket。
        /// </summary>
        private Socket m_scket;

        /// <summary>
        /// 客户端列表。
        /// </summary>
        private readonly List<TcpSession> m_clients;
        /// <summary>
        /// 客户端集合锁。
        /// </summary>
        private readonly object m_clientsLocker;

        private Action<TcpSession, bool> m_sessionChanged;
        /// <summary>
        /// 会话改变通知。
        /// </summary>
        public event Action<TcpSession, bool> SessionChanged
        {
            add { m_sessionChanged += value; }
            remove { m_sessionChanged -= value; }
        }
        /// <summary>
        /// 
        /// </summary>
        public TcpServer()
        {
            m_scket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            m_clients = new List<TcpSession>(); 
            m_clientsLocker = new object();
        }

        /// <summary>
        /// 绑定并监听本地地址。
        /// </summary>
        /// <param name="local"></param>
        /// <returns>失败返回false。</returns>
        public bool Bind(IPEndPoint local)
        {
            try
            {
                m_scket.Bind(local);
                m_scket.Listen(10);
                //启动接收处理
                SocketAsyncEventArgs asyncArgs = new SocketAsyncEventArgs();
                asyncArgs.AcceptSocket = null;
                asyncArgs.Completed += new EventHandler<SocketAsyncEventArgs>(this.OnSocketCompleted);
                if (!m_scket.AcceptAsync(asyncArgs))
                {
                    this.OnSocketCompleted(m_scket, asyncArgs);
                }
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 完成通知。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSocketCompleted(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                //创建会话
                TcpSession session = new TcpSession(e.AcceptSocket);
                session.Disposed += new Action<TcpSession>(ClientDisposed);
                lock (m_clientsLocker)
                {
                    m_clients.Add(session);
                }

                if (m_sessionChanged != null)
                    m_sessionChanged(session, true);

                //继续接收连接
                e.AcceptSocket = null;
                try
                {
                    if (!m_scket.AcceptAsync(e))
                    {
                        this.OnSocketCompleted(sender, e);
                    }
                }
                catch
                {
                    // ExProce
                }
            }
            else
            {
                //add code here
            }
        }

        void ClientDisposed(TcpSession session)
        {
            if (m_sessionChanged != null)
            {
                m_sessionChanged(session, false);
            }
            lock (m_clientsLocker)
            {
                m_clients.Remove(session);
            }
        }


        /// <summary>
        /// 释放。
        /// </summary>
        public void Dispose()
        {
            try
            {
                lock (m_clientsLocker)
                {
                    // 释放客户端会话。
                    foreach (var item in m_clients)
                    {
                        item.Disposed -= ClientDisposed;
                        item.Dispose();
                    }
                    m_clients.Clear();
                }

                //  释放服务的socket。
                if (m_scket != null)
                {
                    m_scket.Dispose();
                    m_scket = null;
                }
            }
            catch
            {
            }
        }
    }
}
